/*
 * Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * openFuyao is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package controller

import (
	"context"
	"fmt"
	"testing"

	"github.com/agiledragon/gomonkey/v2"
	"github.com/stretchr/testify/assert"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"

	kaev1 "gopkg.openfuyao.cn/kae-operator/api/v1"
)

// TestReconcileGetInstanceError 获取实例失败
func TestReconcileGetInstanceError(t *testing.T) {
	ctx := context.TODO()
	// 清理环境
	kaePolicyManager = KAEPolicyManager{}
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}
	req := reconcile.Request{
		NamespacedName: client.ObjectKey{
			Namespace: "default",
			Name:      "test-kae-policy",
		},
	}

	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyMethodReturn(r.Client, "Get",
		errors.NewNotFound(schema.GroupResource{Group: "test", Resource: "test"}, "test"))

	result, err := r.Reconcile(ctx, req)
	assert.Nil(t, err)
	assert.Equal(t, reconcile.Result{}, result)

	patches.Reset()
	patches.ApplyMethodReturn(r.Client, "Get", fmt.Errorf("test err"))
	result, err = r.Reconcile(ctx, req)
	assert.NotNil(t, err)
	assert.Equal(t, reconcile.Result{}, result)
}

// TestReconcileInstanceAlreadyExists 已存在实例，不需要创建新实例
func TestReconcileInstanceAlreadyExists(t *testing.T) {
	patches := gomonkey.NewPatches()
	defer patches.Reset()
	// 清理环境
	kaePolicyManager = KAEPolicyManager{}
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}
	patches.ApplyMethodReturn(r.Client, "Get", nil)
	patches.ApplyMethodReturn(r.Client.Status(), "Update", nil)

	// Mock kaePolicyManager state
	kaePolicyManager.kaeInstance = &kaev1.KAEPolicy{
		ObjectMeta: metav1.ObjectMeta{
			Name: "existing-instance",
		},
	}
	req := reconcile.Request{
		NamespacedName: client.ObjectKey{
			Namespace: "default",
			Name:      "test-kae-exist",
		},
	}
	result, err := r.Reconcile(context.TODO(), req)
	assert.NoError(t, err)
	assert.Equal(t, reconcile.Result{}, result)

}

// TestReconcileInitError 初始化异常
func TestReconcileInitError(t *testing.T) {
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}
	// 环境清理
	kaePolicyManager = KAEPolicyManager{}
	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyMethodReturn(r.Client, "Get", nil)
	patches.ApplyPrivateMethod(&kaePolicyManager, "init", func(_ *KAEPolicyManager, _ context.Context,
		_ *KAEPolicyReconciler, _ *kaev1.KAEPolicy) error {
		return fmt.Errorf("test err")
	})

	// Mock setConditionsError
	patches.ApplyFuncReturn(setConditionsError, nil)
	req := reconcile.Request{
		NamespacedName: client.ObjectKey{
			Namespace: "default",
			Name:      "test-init-err",
		},
	}

	result, err := r.Reconcile(context.TODO(), req)
	assert.Error(t, err)
	assert.Equal(t, reconcile.Result{}, result)

}

// TestReconcileSuccess 协调成功
func TestReconcileSuccess(t *testing.T) {
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}
	patches := gomonkey.NewPatches()
	defer patches.Reset()
	// 环境清理
	kaePolicyManager = KAEPolicyManager{}

	patches.ApplyMethodReturn(r.Client, "Get", nil)
	patches.ApplyPrivateMethod(&kaePolicyManager, "init", func(_ *KAEPolicyManager, _ context.Context,
		_ *KAEPolicyReconciler, _ *kaev1.KAEPolicy) error {
		return nil
	})
	patches.ApplyPrivateMethod(r, "deployAllComponents", func(_ *KAEPolicyReconciler, _ context.Context,
		_ ctrl.Request, _ *kaev1.KAEPolicy) (kaev1.State, []string, error) {
		return kaev1.Ready, []string{}, nil
	})
	patches.ApplyPrivateMethod(r, "updateStateAndConditions", func(_ *KAEPolicyReconciler, _ context.Context,
		_ ctrl.Request, _ kaev1.State, _ []string, _ *kaev1.KAEPolicy) (ctrl.Result, error) {
		return ctrl.Result{}, nil
	})

	req := reconcile.Request{
		NamespacedName: client.ObjectKey{
			Namespace: "default",
			Name:      "test-success",
		},
	}
	result, err := r.Reconcile(context.TODO(), req)
	assert.NoError(t, err)
	assert.Equal(t, reconcile.Result{}, result)
}

// TestUpdateStateAndConditions 状态更新
func TestUpdateStateAndConditions(t *testing.T) {
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}
	req := reconcile.Request{
		NamespacedName: client.ObjectKey{
			Namespace: "default",
			Name:      "AndConditionsNotReady",
		},
	}
	result, err := r.updateStateAndConditions(context.TODO(), req, kaev1.NotReady, []string{"driver"},
		&kaev1.KAEPolicy{})
	assert.Nil(t, err)
	assert.Equal(t, ctrl.Result{RequeueAfter: requeueAfterTime}, result)

	kaePolicyManager = KAEPolicyManager{}
	kaePolicyManager.hasKAENodes = true
	result, err = r.updateStateAndConditions(context.TODO(), req, kaev1.Ready, []string{"driver"},
		&kaev1.KAEPolicy{})
	assert.NotNil(t, err)
	assert.Equal(t, ctrl.Result{}, result)
}

// TestDeployAllComponentsErr 部署错误
func TestDeployAllComponentsErr(t *testing.T) {
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}

	req := ctrl.Request{
		NamespacedName: types.NamespacedName{
			Namespace: "default",
			Name:      "DeployAllComponentsErr",
		},
	}
	instance := &kaev1.KAEPolicy{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "DeployAllComponentsErr",
			Namespace: "default",
		},
	}

	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyPrivateMethod(&kaePolicyManager, "init", func(_ *KAEPolicyManager, _ context.Context,
		_ *KAEPolicyReconciler, _ *kaev1.KAEPolicy) error {
		return nil
	})

	kaePolicyManager.index = 1
	status, _, err := r.deployAllComponents(context.TODO(), req, instance)
	assert.NotNil(t, err)
	assert.Equal(t, kaev1.NotReady, status)
}

func TestDeployAllComponentsNotReady(t *testing.T) {
	r := &KAEPolicyReconciler{
		Client: fake.NewClientBuilder().Build(),
	}

	req := ctrl.Request{
		NamespacedName: types.NamespacedName{
			Namespace: "default",
			Name:      "DeployNotReady",
		},
	}
	instance := &kaev1.KAEPolicy{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "DeployNotReady",
			Namespace: "default",
		},
	}

	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyPrivateMethod(&kaePolicyManager, "init", func(_ *KAEPolicyManager, _ context.Context,
		_ *KAEPolicyReconciler, _ *kaev1.KAEPolicy) error {
		return nil
	})
	patches.ApplyPrivateMethod(&kaePolicyManager, "deployComponent", func(_ *KAEPolicyManager) (kaev1.State, error) {
		return kaev1.NotReady, nil
	})

	kaePolicyManager = KAEPolicyManager{}
	status, _, err := r.deployAllComponents(context.TODO(), req, instance)
	assert.Nil(t, err)
	assert.Equal(t, kaev1.NotReady, status)
}

// TestGetComponentName 组件名称获取
func TestGetComponentName(t *testing.T) {
	kaePolicyManager.componentNames = []string{"component1"}
	tests := []struct {
		name     string
		index    int
		expected string
	}{
		{
			name:     "Valid Index",
			index:    0,
			expected: "component1",
		},
		{
			name:     "Invalid Index",
			index:    2,
			expected: "outbounds",
		},
		{
			name:     "Negative Index",
			index:    -1,
			expected: "outbounds",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := getComponentName(tt.index)
			assert.Equal(t, tt.expected, result)
		})
	}
}
